﻿using AutoMapper;
using Mes.Infrastructure.Data.BoundedContext.Extensions;
using Mes.Infrastructure.Data.BoundedContext.Repositories.Operations;
using Mes.Domain.BoundedContext;
using Mes.Domain.BoundedContext.Entities.Operations;
using Mes.Application.DTO.Inputs.Operations;
using Mes.Application.DTO.Results.Operations;
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;
using Mes.Application.Common;
using Mes.Application.Common.Resource;
using Mes.Infrastructure.Data.BoundedContext.Repositories.MasterData;

namespace Mes.Application.Services.Operations
{
    public class ProcessRouteService : IProcessRouteService
    {
        private readonly IMapper _mapper;
        private readonly IProcessRouteRepository _processRouteRepository;
        private readonly IStdOperRepository _stdOperRepository;
        private readonly IStdOperEntRouteRepository _stdoper_Ent_RouteRepository;
        private readonly IProductionUnitRepository _productionUnitRepository;

        public ProcessRouteService(IMapper mapper, IProcessRouteRepository processRouteRepository, IStdOperRepository stdOperRepository, IStdOperEntRouteRepository stdoper_Ent_RouteRepository, IProductionUnitRepository productionUnitRepository)
        {
            if (mapper == null || processRouteRepository == null || stdOperRepository == null || stdoper_Ent_RouteRepository == null || productionUnitRepository == null)
            {
                throw new ArgumentNullException();
            }
            _mapper = mapper;
            _processRouteRepository = processRouteRepository;
            _stdOperRepository = stdOperRepository;
            _stdoper_Ent_RouteRepository = stdoper_Ent_RouteRepository;
            _productionUnitRepository = productionUnitRepository;
        }

        /// <summary>
        /// 新增工艺路线
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        public async Task AddProcessRouteAsync(AddProcessRouteInput input)
        {
            if (await _processRouteRepository.ExistsAsync(x => x.Code == input.Code || x.Name == input.Name))
            {
                throw new CustomException(Messages.ProcessRouteAlreadyExist);
            }
            if (input.StdOperEntRoutes.Count > 0)
            {
                var operIds = input.StdOperEntRoutes.Select(x => x.Oper_Id).ToList();
                var inputOperIds = input.StdOperEntRoutes.Select(x => x.Input_Oper_Id).ToList();
                if (operIds.Count != operIds.Distinct().Count() || inputOperIds.Count != inputOperIds.Distinct().Count())
                {
                    throw new CustomException(Messages.StdOperDuplication);
                }
                var opersCount = await _stdOperRepository.CountAsync(x => operIds.Contains(x.Oper_Id));
                var inputOpersCount = await _stdOperRepository.CountAsync(x => inputOperIds.Contains(x.Oper_Id));
                if (opersCount != operIds.Count || inputOpersCount != inputOperIds.Count)
                {
                    throw new CustomException(Messages.SomeStdOperNotExist);
                }

                var entIds = input.StdOperEntRoutes.Select(x => x.Ent_Id).ToList();
                var inputEntIds = input.StdOperEntRoutes.Select(x => x.Input_Ent_Id).ToList();
                var allentIds = entIds.Union(inputEntIds);
                if (await _productionUnitRepository.CountAsync(x => allentIds.Contains(x.Id)) != allentIds.Count())
                {
                    throw new CustomException(Messages.SomeProductionUnitNotExist);
                }
            }

            var processRoute = _mapper.Map<ProcessRoute>(input);
            await _processRouteRepository.AddAsync(processRoute);
            int status = await _processRouteRepository.SaveAsync();
            if (status <= 0)
            {
                throw new CustomException(Messages.ErrorSavingOper);
            }
            if (input.StdOperEntRoutes.Count > 0)
            {
                var routes = new List<StdOperEntRoute>();
                foreach (var item in input.StdOperEntRoutes)
                {
                    var route = _mapper.Map<StdOperEntRoute>(item);
                    route.Process_Route_Id = processRoute.Id;
                    routes.Add(route);
                }
                await _stdoper_Ent_RouteRepository.AddRangeAsync(routes);
                status = await _stdoper_Ent_RouteRepository.SaveAsync();
                if (status <= 0)
                {
                    throw new CustomException(Messages.SavingFailure);
                }
            }
        }

        /// <summary>
        /// 编辑工艺路线
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        public async Task UpdateProcessRouteAsync(UpdateProcessRouteInput input)
        {
            var processRoute = await _processRouteRepository.FindAsync(x => x.Id == input.ProcessRouteId);
            if (processRoute == null)
            {
                throw new CustomException(Messages.ProcessRouteNotFound);
            }
            if (input.StdOperEntRoutes.Count > 0)
            {
                var operIds = input.StdOperEntRoutes.Select(x => x.Oper_Id).ToList();
                var inputOperIds = input.StdOperEntRoutes.Select(x => x.Input_Oper_Id).ToList();
                if (operIds.Count != operIds.Distinct().Count() || inputOperIds.Count != inputOperIds.Distinct().Count())
                {
                    throw new CustomException(Messages.StdOperDuplication);
                }
                var opersCount = await _stdOperRepository.CountAsync(x => operIds.Contains(x.Oper_Id));
                var inputOpersCount = await _stdOperRepository.CountAsync(x => inputOperIds.Contains(x.Oper_Id));
                if (opersCount != operIds.Count || inputOpersCount != inputOperIds.Count)
                {
                    throw new CustomException(Messages.SomeStdOperNotExist);
                }
            }

            processRoute.Remark = input.Remark;
            await _stdoper_Ent_RouteRepository.DeleteAsync(x => x.Process_Route_Id == input.ProcessRouteId);
            if (input.StdOperEntRoutes.Count > 0)
            {
                var routes = new List<StdOperEntRoute>();
                foreach (var item in input.StdOperEntRoutes)
                {
                    var route = _mapper.Map<StdOperEntRoute>(item);
                    route.Process_Route_Id = processRoute.Id;
                    routes.Add(route);
                }
                await _stdoper_Ent_RouteRepository.AddRangeAsync(routes);
            }
            int status = await _stdoper_Ent_RouteRepository.SaveAsync();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
        }

        /// <summary>
        /// 删除工艺路线
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        public async Task DeleteProcessRoutesAsync(IList<int> ids)
        {
            await _processRouteRepository.DeleteAsync(x => ids.Contains(x.Id));
            await _stdoper_Ent_RouteRepository.DeleteAsync(x => ids.Contains(x.Process_Route_Id));
            int status = await _processRouteRepository.SaveAsync();
            if (status <= 0)
            {
                throw new CustomException(Messages.SavingFailure);
            }
        }

        /// <summary>
        /// 分页查询工艺路线
        /// </summary>
        /// <param name="input"></param>
        /// <returns>PagedItemsData<ProcessRoutesResult></returns>
        /// <exception cref="CustomException"></exception>
        public async Task<PagedItemsData<ProcessRoutesResult>> GetProcessRoutesAsync(GetProcessRoutesInput input)
        {
            Expression<Func<ProcessRoute, bool>> filter = x => true;
            if (!string.IsNullOrEmpty(input.KeyWord))
            {
                filter = filter.And(x => x.Name.Contains(input.KeyWord) || x.Code.Contains(input.KeyWord));
            }
            var data = await _processRouteRepository.Query().Where(filter).OrderByDescending(x => x.CreateTime).ToPagedAsync(input.PagedInput);
            if (data == null)
            {
                throw new CustomException(Messages.ProcessRouteNotFound);
            }
            var result = _mapper.Map<PagedItemsData<ProcessRoutesResult>>(data);
            var processRouteIds = result.Items.Select(x => x.Id).ToList();
            return result;
        }

        /// <summary>
        /// 查询工艺路线
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        /// <exception cref="CustomException"></exception>
        public async Task<ProcessRouteResult> GetProcessRouteAsync(int id)
        {
            var processRoute = await _processRouteRepository.FindAsync(x => x.Id == id);
            if (processRoute == null)
            {
                throw new CustomException(Messages.ProcessRouteNotFound);
            }
            var result = _mapper.Map<ProcessRouteResult>(processRoute);
            var operEntRoutes = await _stdoper_Ent_RouteRepository.Query().Where(x => x.Process_Route_Id == id).OrderBy(x => x.Order).ToListAsync();
            if (operEntRoutes.Count() > 0)
            {
                if (operEntRoutes.Count() == 1 && operEntRoutes.FirstOrDefault().Input_Oper_Id == operEntRoutes.FirstOrDefault().Oper_Id)
                {
                    //工艺路线只有一个工序
                    result.StdOperEntRouteResults = _mapper.Map<List<StdOperEntRouteResult>>(operEntRoutes).ToList();
                    var inputOperId = operEntRoutes.FirstOrDefault().Input_Oper_Id;
                    var InputOper = await _stdOperRepository.FindAsync(x => x.Oper_Id == inputOperId);
                    var InputEntId = operEntRoutes.FirstOrDefault().Input_Ent_Id;
                    var InputEnt = await _productionUnitRepository.FindAsync(x => x.Id == InputEntId);
                    result.StdOperEntRouteResults.FirstOrDefault().Input_Ent_Name = InputEnt.ProductionUnitName;
                    result.StdOperEntRouteResults.FirstOrDefault().Input_Oper_Desc = InputOper.Oper_Desc;
                }
                else
                {
                    result.StdOperEntRouteResults = _mapper.Map<List<StdOperEntRouteResult>>(operEntRoutes).ToList();
                    var allOperIds = new List<string>();
                    var InputOperIds = operEntRoutes.Select(x => x.Input_Oper_Id).ToList();
                    var lastOperId = operEntRoutes.Select(x => x.Oper_Id).Last();
                    foreach (var item in InputOperIds)
                    {
                        allOperIds.Add(item);
                    }
                    allOperIds.Add(lastOperId);

                    var allEntIds = new List<int>();
                    var InputEntIds = operEntRoutes.Select(x => x.Input_Ent_Id).ToList();
                    var lastEntId = operEntRoutes.Select(x => x.Ent_Id).Last();
                    foreach (var item in InputEntIds)
                    {
                        allEntIds.Add(item);
                    }
                    allEntIds.Add(lastEntId);

                    var allOpers = await _stdOperRepository.Query().Where(x => allOperIds.Contains(x.Oper_Id)).ToListAsync();
                    var allEnts = await _productionUnitRepository.Query().Where(x => allEntIds.Contains(x.Id)).ToListAsync();
                    foreach (var item in result.StdOperEntRouteResults)
                    {
                        item.Input_Oper_Desc = allOpers.FirstOrDefault(x => x.Oper_Id == item.Input_Oper_Id)?.Oper_Desc ?? String.Empty;
                        item.Input_Ent_Name = allEnts.FirstOrDefault(x => x.Id == item.Input_Ent_Id)?.ProductionUnitName ?? String.Empty;
                    }

                    var lastOper_Desc = allOpers.FirstOrDefault(x => x.Oper_Id == lastOperId)?.Oper_Desc ?? String.Empty;
                    var lastEnt_Name = allEnts.FirstOrDefault(x => x.Id == lastEntId)?.ProductionUnitName ?? String.Empty;

                    var lastResult = new StdOperEntRouteResult { Input_Oper_Id = lastOperId, Input_Oper_Desc = lastOper_Desc, Input_Ent_Id = lastEntId, Input_Ent_Name = lastEnt_Name, Order = result.StdOperEntRouteResults.Last().Order + 1 };
                    result.StdOperEntRouteResults.Add(lastResult);
                }
            }         
            return result;
        }

        /// <summary>
        /// 查询工艺路线
        /// </summary>
        /// <param name="ids"></param>
        /// <returns>ItemsResult<ProcessRoutesResult></returns>
        /// <exception cref="CustomException"></exception>
        public async Task<ItemsResult<ProcessRoutesResult>> GetProcessRoutesByIdsAsync(IList<int> ids)
        {
            var data = await _processRouteRepository.Query().Where(x => ids.Contains(x.Id)).OrderByDescending(x => x.CreateTime).ToListAsync();
            if (data.Count == 0)
            {
                throw new CustomException(Messages.ProcessRouteNotFound);
            }
            return _mapper.Map<ItemsResult<ProcessRoutesResult>>(data);
        }
    }
}
